package com.jason.common.file.word;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.*;
import com.jason.common.core.exception.BizException;
import com.jason.common.file.word.dto.WordImportQuestionData;
import com.jason.common.file.word.dto.WordPictureInfo;
import com.jason.common.file.word.dto.WordTemplateTree;
import com.jason.common.file.word.extension.WordPictureUploadInterface;
import com.jason.common.file.word.resolver.WordTextResolverInterface;
import com.jason.common.file.word.resolver.WordXmlResolver;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 默认实现
 *
 * @author guozhongcheng
 * @since 2023/9/28
 **/
@Slf4j
@SuppressWarnings("unused")
public class XmlWordImportQuestionContext extends AbstractWordImportQuestionContext {
    /**
     * 利用好其预编译功能，可以有效加快正则匹配速度
     */
    public final static Pattern DYNAMIC = Pattern.compile("\\$\\{([A-Za-z0-9]+-[0-9]+)\\}");
    public final static Pattern DYNAMIC_LIMIT_COUNT = Pattern.compile("\\$\\{([A-Za-z0-9]+-[0-9]+)\\}");

    /**
     * 解压父级目录
     */
    protected final static String WORD_UNZIP_DIR = "word";
    /**
     * 内部图片、音频、视频等文件的目录
     */
    protected final static String WORD_INNER_FILE_DIR = WORD_UNZIP_DIR + File.separator + "media";
    /**
     * 保存着Word文档内部所有文本信息和文件信息的Xml文件路径
     */
    protected final static String WORD_XML_FILE_PATH = WORD_UNZIP_DIR + File.separator + "document.xml";
    /**
     * 保存着Word文档内容文件位置映射信息的Xml文件路径
     */
    protected final static String WORD_MAPPING_INFO_PATH = WORD_UNZIP_DIR + File.separator + "_rels" + File.separator + "document.xml.rels";
    /**
     * 本地生成Word文档的基础目录
     */
    protected String localSaveWordBaseDir = System.getProperty("java.io.tmpdir");
    /**
     * 本地生成Word文档的文件名称
     */
    protected String localSaveWordFileName = RandomUtil.randomString(6) + "-" + DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss");
    /**
     * 本地Word文档解压后的基础目录
     */
    protected String wordUnzipBaseDir;

    /**
     * Word文本解析器接口
     */
    protected final WordTextResolverInterface wordTextResolverInterface;

    /**
     * Word文档Xml解析器
     */
    protected WordXmlResolver wordXmlResolver;

    public XmlWordImportQuestionContext(String totalTaskName, Class<?> templateDefinitionClass, InputStream docxFileInputStream, WordTextResolverInterface wordTextResolverInterface) {
        super(totalTaskName, templateDefinitionClass, docxFileInputStream);
        this.wordTextResolverInterface = wordTextResolverInterface;
    }

    @Override
    protected void preCheck(InputStream docxFileInputStream) throws Exception {
        super.preCheck(docxFileInputStream);
        if (StrUtil.isBlank(this.localSaveWordBaseDir)) {
            this.localSaveWordBaseDir = System.getProperty("user.dir");
            if (StrUtil.isBlank(this.localSaveWordBaseDir)) {
                throw new Exception("生成本地word文档路径为空");
            }
        }
        if (StrUtil.isBlank(this.localSaveWordFileName)) {
            throw new Exception("生成本地word文档文件名称为空");
        }
        Assert.notNull(this.wordTextResolverInterface, "未实现 WordTextResolverInterface 接口");
    }

    @Override
    protected void loadPrepareData(InputStream docxFileInputStream) {
        String tempSaveWordDir = this.localSaveWordBaseDir + File.separator + this.localSaveWordFileName;
        // 生成本地word目录
        File file = FileUtil.mkdir(tempSaveWordDir);
        // 解压word文档
        File unzipFile = ZipUtil.unzip(docxFileInputStream, file, CharsetUtil.CHARSET_UTF_8);
        this.wordUnzipBaseDir = unzipFile.getAbsolutePath();
        Assert.notBlank(this.wordUnzipBaseDir, "word文档解压基础目录为空");
    }

    @Override
    protected CompletableFuture<Map<String, String>> asyncLoadInnerFileMapping() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return this.loadInnerFileMapping();
            } catch (Exception e) {
                throw new BizException("加载图片、视频等文件映射数据发生异常", e);
            }
        }, super.threadPoolExecutor);
    }

    @Override
    protected Map<String, String> loadInnerFileMapping() throws Exception {
        String id = "加载图片、视频等文件映射数据";
        StopWatch stopWatch = new StopWatch(id);
        stopWatch.start(id);
        String fullMappingFilePath = this.wordUnzipBaseDir + File.separator + WORD_MAPPING_INFO_PATH;
        // 解析文件映射集合
        Map<String, String> mappingMap = new WordXmlResolver().parseMapping(fullMappingFilePath);
        stopWatch.stop();
        super.STOP_WATCH.put(id, stopWatch);
        return mappingMap;
    }

    @Override
    protected CompletableFuture<String> asyncLoadWordText(Class<?> templateDefinitionClass) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return this.loadWordText(templateDefinitionClass);
            } catch (Exception e) {
                throw new BizException("加载Word文档文件文本数据发生异常", e);
            }
        }, super.threadPoolExecutor);
    }

    @Override
    protected String loadWordText(Class<?> templateDefinitionClass) throws Exception {
        String id = "加载Word文档文件文本数据";
        StopWatch stopWatch = new StopWatch(id);
        stopWatch.start(id);
        String fullWordTextContentXmlFilePath = this.wordUnzipBaseDir + File.separator + WORD_XML_FILE_PATH;
        WordXmlResolver wordXmlResolver = new WordXmlResolver();
        String wordText = wordXmlResolver.parseContent(fullWordTextContentXmlFilePath);
        Assert.notBlank(wordText, "解析word文档文本字符串为空");
        this.wordXmlResolver = wordXmlResolver;
        super.WORD_PIC_INFO_MAP.putAll(wordXmlResolver.getWordPicInfoMap());
        stopWatch.stop();
        super.STOP_WATCH.put(id, stopWatch);
        return wordText;
    }

    @Override
    protected void preProcessor() {
        Assert.notBlank(super.wordTextStr, "word文本内容为空");
        Assert.notNull(this.wordXmlResolver, "加载wordXml解析器为空");
        Assert.notNull(super.wordTemplateTree, "加载的模板树结构为空");
    }

    @Override
    protected void parseObj(List<WordImportQuestionData> questionDataList, WordTemplateTree wordTemplateTree) throws Throwable {
        List<WordImportQuestionData> parseList;
        // 解析word文本内容
        if (super.enableConcurrent) {
            parseList = this.wordTextResolverInterface.concurrentParse(super.wordTextStr, wordTemplateTree.getTemplateTreeNodeList(), super.threadPoolExecutor);
        } else {
            parseList = this.wordTextResolverInterface.parse(super.wordTextStr, wordTemplateTree.getTemplateTreeNodeList());
        }
        Assert.notEmpty(parseList, "解析后的试题对象列表为空");
        questionDataList.addAll(parseList);
    }

    /**
     * 用于清洗数据和替换对应占位符的数据
     */
    @Override
    @SuppressWarnings("unchecked")
    protected void postProcessor(List<WordImportQuestionData> questionDataList) throws Exception {
        // 转换内部文件映射集合
        this.convertWordInnerFileMappingMap();
        // 遍历
        for (WordImportQuestionData data : questionDataList) {
            for (Field field : ReflectUtil.getFields(data.getClass())) {
                field.setAccessible(true);
                if (String.class.equals(field.getType())) {
                    String valueStr = (String) field.get(data);
                    if (StrUtil.isBlank(valueStr)) {
                        continue;
                    }
                    // 填充占位符
                    if (StrUtil.isNotBlank(valueStr)) {
                        this.populatePlaceHolder(data, field, valueStr, false);
                    }
                }
                // 填充选项数据列表数据
                if (field.getName().equals(LambdaUtil.getFieldName(WordImportQuestionData::getOptionDataList))) {
                    List<WordImportQuestionData.OptionData> optionDataList = (List<WordImportQuestionData.OptionData>) field.get(data);
                    if (CollUtil.isEmpty(optionDataList)) {
                        continue;
                    }
                    // 填充选项列表中的占位符
                    this.populateOptionPlaceHolder(optionDataList);
                    ReflectUtil.setFieldValue(data, LambdaUtil.getFieldName(WordImportQuestionData::getOptionDataList), optionDataList);
                }
            }
        }
    }

    /**
     * 转换内部文件映射集合
     */
    private void convertWordInnerFileMappingMap() {
        if (CollUtil.isNotEmpty(this.MAPPING_INFO_MAP)) {
            Map<String, String> remoteFileMappingMap = new HashMap<>(16);
            for (WordPictureInfo pictureInfo : super.WORD_PIC_INFO_MAP.values()) {
                String sourcePictureLocalPath = this.MAPPING_INFO_MAP.get(pictureInfo.getDistinctId());
                Assert.notBlank(sourcePictureLocalPath, "未找到源图片本地保存路径");
                String fullSourcePictureLocalPath = this.wordUnzipBaseDir + File.separator + WORD_UNZIP_DIR + File.separator + sourcePictureLocalPath;
                pictureInfo.setSourcePictureLocalPath(fullSourcePictureLocalPath);
            }
            if (super.enableFileRemoteMapping) {
                // 上传文件到服务器
                if (super.enableConcurrent) {
                    remoteFileMappingMap.putAll(super.wordPictureUploadInterface.concurrentUploadPic(super.WORD_PIC_INFO_MAP, super.threadPoolExecutor));
                } else {
                    remoteFileMappingMap.putAll(super.wordPictureUploadInterface.uploadPic(super.WORD_PIC_INFO_MAP));
                }
            } else {
                for (String key : super.WORD_PIC_INFO_MAP.keySet()) {
                    String tempLocalFilePath = super.WORD_PIC_INFO_MAP.get(key).getSourcePictureLocalPath();
                    // 根据资源路径获取文件类型
                    String typeByPath = FileTypeUtil.getTypeByPath(tempLocalFilePath);
                    // 拼接文件保存本地的完整路径
                    String fullSaveLocalFilePath = super.localSaveFileBaseDir + File.separator
                            + RandomUtil.randomString(8) + "-" + DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + StrPool.DOT + typeByPath;
                    FileUtil.copyFile(tempLocalFilePath, fullSaveLocalFilePath);
                    remoteFileMappingMap.put(key, fullSaveLocalFilePath);
                }
            }
            super.REMOTE_FILE_MAPPING_MAP.putAll(remoteFileMappingMap);
        }
    }

    /**
     * 填充选项中的占位符
     *
     * @param optionDataList 选项数据列表
     * @throws Exception 填充异常
     */
    private void populateOptionPlaceHolder(List<WordImportQuestionData.OptionData> optionDataList) throws Exception {
        for (WordImportQuestionData.OptionData optionData : optionDataList) {
            for (Field field : ReflectUtil.getFields(optionData.getClass())) {
                field.setAccessible(true);
                if (String.class.equals(field.getType())) {
                    String valueStr = (String) field.get(optionData);
                    if (StrUtil.isBlank(valueStr)) {
                        continue;
                    }
                    // 前置处理选项数据
                    this.preProcessorOptionData(optionData, field, valueStr);
                    // 填充占位符
                    this.populatePlaceHolder(optionData, field, valueStr, true);
                }
            }
        }
    }

    /**
     * 前置处理选项数据，清洗数据和处理字段值
     *
     * @param optionData 选项数据
     * @param field      属性对象
     * @param valueStr   属性值
     * @throws IllegalAccessException 参数访问异常
     */
    private void preProcessorOptionData(WordImportQuestionData.OptionData optionData, Field field, String valueStr) throws IllegalAccessException {
        valueStr = valueStr.trim();
        field.set(optionData, valueStr);
        if (!this.isHavePlaceHolder(valueStr) && field.getName().equals(LambdaUtil.getFieldName(WordImportQuestionData.OptionData::getContent))) {
            optionData.setText(valueStr);
        }
    }

    /**
     * 填充占位符
     *
     * @param data     实例对象
     * @param field    属性对象
     * @param valueStr 属性值
     * @param isOption 是否选项类型
     * @throws Exception 填充异常
     */
    private void populatePlaceHolder(Object data, Field field, String valueStr, boolean isOption) throws Exception {
        // 清洗数据
        valueStr = this.cleanTextData(valueStr);
        // 将换行符替换成富文本的换行符<br/>
        valueStr = valueStr.replaceAll("\n", "<br/>");

        // 判断是否含有占位符
        List<String> keyList = this.getPlaceHolderKeyList(valueStr);
        // 获取所有占位符key列表
        if (CollUtil.isEmpty(keyList)) {
            field.set(data, valueStr);
            return;
        }
        String replacementResultStr = valueStr;
        String replacementOptionText = valueStr;
        List<String> optionPictureUrlList = new ArrayList<>(16);
        for (String key : keyList) {
            // 获取图片信息
            WordPictureInfo wordPictureInfo = Assert.notNull(super.WORD_PIC_INFO_MAP.get(key), "图片ID【{}】获取图片信息为空", key);
            String picUrl = Assert.notBlank(wordPictureInfo.getRemoteSaveUrl(), "图片ID【{}】获取图片资源存储地址为空", key);
            // 是否选项数据
            if (isOption) {
                // 拆分文字和图片
                replacementOptionText = replacementOptionText.replace("${" + key + "}", "");
                optionPictureUrlList.add(picUrl);
            } else {
                int width = wordPictureInfo.getWidth() == 0 ? super.minPicWidth : wordPictureInfo.getWidth();
                int height = wordPictureInfo.getHeight() == 0 ? super.minPicHeight : wordPictureInfo.getHeight();
                if (width > super.maxPicWidth) {
                    width = super.maxPicWidth;
                }
                if (height > super.maxPicHeight) {
                    height = super.maxPicHeight;
                }
                if (super.fixedPicWidth > 0) {
                    width = super.fixedPicWidth;
                }
                if (super.fixedPicHeight > 0) {
                    height = super.fixedPicHeight;
                }
//                String replacement = "<p><img src=\"" + picUrl
//                        + "\" style=\"width:" + width + "px;"
//                        + "height:" + height + "px"
//                        + "\"/></p>";
                String replacement = "<img src=\"" + picUrl + "\"/>";
                replacementResultStr = replacementResultStr.replace("${" + key + "}", replacement);
            }
        }
        // 填充字段值
        if (isOption) {
            // 赋值文本
            String optionTextFieldName = LambdaUtil.getFieldName(WordImportQuestionData.OptionData::getText);
            ReflectUtil.setFieldValue(data, optionTextFieldName, replacementOptionText);
            // 赋值图片
            String pictureUrlListFieldName = LambdaUtil.getFieldName(WordImportQuestionData.OptionData::getPictureUrlList);
            ReflectUtil.setFieldValue(data, pictureUrlListFieldName, optionPictureUrlList);
        } else {
            field.set(data, replacementResultStr);
        }
    }

    @Override
    protected void close(InputStream docxFileInputStream) throws IOException {
        super.close(docxFileInputStream);
        if (!FileUtil.del(this.wordUnzipBaseDir)) {
            throw new BizException("删除本地临时Word文档解压目录失败");
        }
    }

    public void setLocalSaveWordBaseDir(String localSaveWordBaseDir) {
        this.localSaveWordBaseDir = localSaveWordBaseDir;
    }

    public void setLocalSaveWordFileName(String localSaveWordFileName) {
        this.localSaveWordFileName = localSaveWordFileName;
    }

    @Override
    public void setWordPicUploadInterface(WordPictureUploadInterface wordPictureUploadInterface) {
        this.wordPictureUploadInterface = wordPictureUploadInterface;
    }

    @Override
    public void setFixedPicWidth(int fixedPicWidth) {
        super.setFixedPicWidth(fixedPicWidth);
    }

    @Override
    public void setFixedPicHeight(int fixedPicHeight) {
        super.setFixedPicHeight(fixedPicHeight);
    }

    @Override
    public void setLocalSaveFileBaseDir(String localSaveFileBaseDir) {
        super.setLocalSaveFileBaseDir(localSaveFileBaseDir);
    }

    /**
     * 判断内容中是否包含动态参数(${key}形式的)
     *
     * @param content 要判断的内容
     * @return 是否有占位符
     */
    private boolean isHavePlaceHolder(String content) {
        return DYNAMIC.matcher(content).matches();
    }

    /**
     * 按照动态内容的参数出现顺序,将参数放到List中
     *
     * @param content 需要匹配的字符串
     * @return 匹配到的列表
     */
    private List<String> getPlaceHolderKeyList(String content) {
        Set<String> paramSet = new LinkedHashSet<>(16);
        Matcher m = DYNAMIC_LIMIT_COUNT.matcher(content);
        while (m.find()) {
            paramSet.add(m.group(1));
        }
        return new ArrayList<>(paramSet);
    }

    /**
     * 清洗文本数据，主要清除换行符和文本首尾的空格
     *
     * @param text 文本字符串
     * @return 清洗后的文本
     */
    private String cleanTextData(String text) {
        if (text == null) {
            return "";
        }
        return text.trim();
    }
}
